import { hashPassword, verifyPassword, generateSalt } from '../utils/crypto.js';
import { generateTokenPayload, calculateExpiryTimestamp } from '../utils/jwt.js';
import { generateRecoveryCodes, hashRecoveryCode } from '../utils/crypto.js';
import { 
  AuthenticationError, 
  ConflictError, 
  ValidationError,
  NotFoundError 
} from '../utils/errors.js';
import { validatePasswordStrength, validateEmail, validateUsername } from '../utils/validation.js';
import { config } from '../config/index.js';
import { logAudit, logger } from '../utils/logger.js';

export class AuthService {
  constructor(prisma, jwt) {
    this.prisma = prisma;
    this.jwt = jwt;
  }

  /**
   * 用户注册
   */
  async register(data, ipAddress, userAgent) {
    const { username, email, password } = data;
    
    // 验证用户名
    const usernameValidation = validateUsername(username);
    if (!usernameValidation.valid) {
      throw new ValidationError('INVALID_INPUT', usernameValidation.errors.join(', '));
    }
    
    // 验证邮箱
    if (!validateEmail(email)) {
      throw new ValidationError('INVALID_INPUT', '邮箱格式不正确');
    }
    
    // 验证密码强度
    const passwordValidation = validatePasswordStrength(password);
    if (!passwordValidation.valid) {
      throw new ValidationError('PASSWORD_TOO_WEAK', passwordValidation.errors.join(', '));
    }
    
    // 检查用户名是否已存在
    const existingUser = await this.prisma.user.findFirst({
      where: {
        OR: [
          { username },
          { email },
        ],
      },
    });
    
    if (existingUser) {
      if (existingUser.username === username) {
        throw new ConflictError('DUPLICATE_ENTRY', '用户名已被使用');
      }
      if (existingUser.email === email) {
        throw new ConflictError('DUPLICATE_ENTRY', '邮箱已被使用');
      }
    }
    
    // 创建用户
    const passwordHash = await hashPassword(password);
    const masterKeySalt = generateSalt();
    
    const user = await this.prisma.user.create({
      data: {
        username,
        email,
        passwordHash,
        masterKeySalt,
        role: 'user',
      },
      select: {
        id: true,
        username: true,
        email: true,
        role: true,
        createdAt: true,
      },
    });
    
    // 创建默认偏好设置
    await this.prisma.userPreference.create({
      data: {
        userId: user.id,
      },
    });
    
    // 记录审计日志
    await logAudit(this.prisma, {
      userId: user.id,
      action: 'USER_REGISTER',
      resourceType: 'user',
      resourceId: user.id,
      ipAddress,
      userAgent,
      status: 'success',
    });
    
    logger.info('User registered', { userId: user.id, username });
    
    return user;
  }

  /**
   * 用户登录
   */
  async login(data, ipAddress, userAgent) {
    const { username, email, password } = data;
    
    // 查找用户
    const user = await this.prisma.user.findFirst({
      where: {
        OR: [
          { username: username || '' },
          { email: email || '' },
        ],
      },
    });
    
    if (!user) {
      throw new AuthenticationError('INVALID_CREDENTIALS', '用户名或密码错误');
    }
    
    // 检查账户是否被禁用
    if (!user.isActive) {
      throw new AuthenticationError('ACCOUNT_DISABLED', '账户已被禁用');
    }
    
    // 检查账户是否被锁定
    if (user.lockedUntil && new Date(user.lockedUntil) > new Date()) {
      const remainingMinutes = Math.ceil((new Date(user.lockedUntil) - new Date()) / 60000);
      throw new AuthenticationError(
        'ACCOUNT_LOCKED', 
        `账户已被锁定，请在${remainingMinutes}分钟后重试`
      );
    }
    
    // 验证密码
    const isPasswordValid = await verifyPassword(password, user.passwordHash);
    
    if (!isPasswordValid) {
      // 增加登录失败次数
      const newAttempts = user.loginAttempts + 1;
      const updateData = {
        loginAttempts: newAttempts,
      };
      
      // 如果失败次数超过限制，锁定账户
      if (newAttempts >= config.security.maxLoginAttempts) {
        updateData.lockedUntil = new Date(
          Date.now() + config.security.lockoutDuration * 60000
        );
      }
      
      await this.prisma.user.update({
        where: { id: user.id },
        data: updateData,
      });
      
      // 记录失败日志
      await logAudit(this.prisma, {
        userId: user.id,
        action: 'LOGIN_FAILED',
        ipAddress,
        userAgent,
        status: 'failed',
        details: { attempts: newAttempts },
      });
      
      throw new AuthenticationError('INVALID_CREDENTIALS', '用户名或密码错误');
    }
    
    // 重置登录失败次数
    await this.prisma.user.update({
      where: { id: user.id },
      data: {
        loginAttempts: 0,
        lockedUntil: null,
        lastLogin: new Date(),
      },
    });
    
    // 生成JWT
    const payload = generateTokenPayload(user);
    const accessToken = this.jwt.sign(payload, { expiresIn: config.jwt.expiresIn });
    const refreshToken = this.jwt.sign(
      { userId: user.id, type: 'refresh' }, 
      { expiresIn: config.jwt.refreshExpiresIn }
    );
    
    // 保存会话
    const session = await this.createSession(user.id, refreshToken, {
      ipAddress,
      userAgent,
    });
    
    // 记录成功日志
    await logAudit(this.prisma, {
      userId: user.id,
      action: 'LOGIN_SUCCESS',
      resourceType: 'session',
      resourceId: session.id,
      ipAddress,
      userAgent,
      status: 'success',
    });
    
    logger.info('User logged in', { userId: user.id, username: user.username });
    
    return {
      user: {
        id: user.id,
        username: user.username,
        email: user.email,
        role: user.role,
      },
      accessToken,
      refreshToken,
      sessionId: session.id,
    };
  }

  /**
   * 创建会话
   */
  async createSession(userId, refreshToken, deviceInfo = {}) {
    const bcrypt = await import('bcrypt');
    const refreshTokenHash = await bcrypt.hash(refreshToken, 10);
    
    // 检查活跃会话数量
    const activeSessions = await this.prisma.userSession.count({
      where: {
        userId,
        isActive: true,
        expiresAt: { gte: new Date() },
      },
    });
    
    // 如果超过最大设备数，删除最旧的会话
    if (activeSessions >= config.session.maxDevices) {
      const oldestSession = await this.prisma.userSession.findFirst({
        where: {
          userId,
          isActive: true,
        },
        orderBy: { lastActivity: 'asc' },
      });
      
      if (oldestSession) {
        await this.prisma.userSession.delete({
          where: { id: oldestSession.id },
        });
      }
    }
    
    // 创建新会话
    const expiresAt = calculateExpiryTimestamp(config.jwt.refreshExpiresIn);
    
    return this.prisma.userSession.create({
      data: {
        userId,
        refreshTokenHash,
        ipAddress: deviceInfo.ipAddress,
        userAgent: deviceInfo.userAgent,
        expiresAt,
      },
    });
  }

  /**
   * 刷新Token
   */
  async refreshToken(refreshToken, ipAddress, userAgent) {
    try {
      const decoded = this.jwt.verify(refreshToken);
      
      if (decoded.type !== 'refresh') {
        throw new Error('Invalid token type');
      }
      
      // 查找会话
      const sessions = await this.prisma.userSession.findMany({
        where: {
          userId: decoded.userId,
          isActive: true,
          expiresAt: { gte: new Date() },
        },
      });
      
      const bcrypt = await import('bcrypt');
      let validSession = null;
      
      for (const session of sessions) {
        const isValid = await bcrypt.compare(refreshToken, session.refreshTokenHash);
        if (isValid) {
          validSession = session;
          break;
        }
      }
      
      if (!validSession) {
        throw new Error('Session not found');
      }
      
      // 获取用户信息
      const user = await this.prisma.user.findUnique({
        where: { id: decoded.userId },
      });
      
      if (!user || !user.isActive) {
        throw new Error('User not found or inactive');
      }
      
      // 生成新的access token
      const payload = generateTokenPayload(user);
      const newAccessToken = this.jwt.sign(payload, { expiresIn: config.jwt.expiresIn });
      
      // 更新会话活动时间
      await this.prisma.userSession.update({
        where: { id: validSession.id },
        data: { lastActivity: new Date() },
      });
      
      return {
        accessToken: newAccessToken,
        user: {
          id: user.id,
          username: user.username,
          email: user.email,
          role: user.role,
        },
      };
    } catch (error) {
      throw new AuthenticationError('TOKEN_INVALID', 'Invalid or expired refresh token');
    }
  }

  /**
   * 登出
   */
  async logout(userId, sessionId) {
    await this.prisma.userSession.deleteMany({
      where: {
        userId,
        id: sessionId,
      },
    });
    
    logger.info('User logged out', { userId, sessionId });
  }

  /**
   * 登出所有设备
   */
  async logoutAll(userId) {
    const result = await this.prisma.userSession.deleteMany({
      where: { userId },
    });
    
    logger.info('User logged out from all devices', { userId, count: result.count });
    
    return result.count;
  }

  /**
   * 修改密码
   */
  async changePassword(userId, oldPassword, newPassword) {
    const user = await this.prisma.user.findUnique({
      where: { id: userId },
    });
    
    if (!user) {
      throw new NotFoundError('USER_NOT_FOUND', '用户不存在');
    }
    
    // 验证旧密码
    const isOldPasswordValid = await verifyPassword(oldPassword, user.passwordHash);
    if (!isOldPasswordValid) {
      throw new AuthenticationError('INVALID_CREDENTIALS', '原密码错误');
    }
    
    // 验证新密码强度
    const passwordValidation = validatePasswordStrength(newPassword);
    if (!passwordValidation.valid) {
      throw new ValidationError('PASSWORD_TOO_WEAK', passwordValidation.errors.join(', '));
    }
    
    // 更新密码
    const newPasswordHash = await hashPassword(newPassword);
    await this.prisma.user.update({
      where: { id: userId },
      data: { passwordHash: newPasswordHash },
    });
    
    // 登出所有设备（强制重新登录）
    await this.logoutAll(userId);
    
    // 记录审计日志
    await logAudit(this.prisma, {
      userId,
      action: 'PASSWORD_CHANGED',
      status: 'success',
    });
    
    logger.info('User changed password', { userId });
  }

  /**
   * 生成恢复码
   */
  async generateRecoveryCodes(userId) {
    // 删除旧的恢复码
    await this.prisma.recoveryCode.deleteMany({
      where: { userId },
    });
    
    // 生成新的恢复码
    const codes = generateRecoveryCodes(10);
    const hashedCodes = await Promise.all(
      codes.map(async (code) => ({
        userId,
        codeHash: await hashRecoveryCode(code),
      }))
    );
    
    // 保存到数据库
    await this.prisma.recoveryCode.createMany({
      data: hashedCodes,
    });
    
    logger.info('Recovery codes generated', { userId });
    
    // 返回明文恢复码（仅此一次）
    return codes;
  }
}


